import gui.MotifGraph;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import clustering.KMeans;
import clustering.Vector;


public class tester 
{
	private static final double DIFF_THRESHOLD = 0.4;
	private static final double HIGH_VALUE_THRESHOLD = 0.96;
	private static final double LOW_VALUE_THRESHOLD = 0.04;
	
	private static int CLUSTERS_SIZE = 149;
	
	private static boolean OUT_FILE = true;
	private static boolean initialized = false;
	
	
	private static HashMap<Integer, SNPData> m_snps;
	private static List<Integer> m_significant;
	private static List<PersonData> m_testCases;
	
	final static private String ROOT_DIRECTORY = "D:\\dev\\Java\\computational-human-genetics-project\\";

	public static void main(String[] args)
    {
		execute_main(args);
    }
    
	public static double execute_main(String[] args)
	{
		m_snps = new HashMap<Integer, SNPData>();
		m_significant = new ArrayList<Integer>();
		m_testCases = new ArrayList<PersonData>();
		//CLUSTERS_SIZE = PropertiesObj.GetPropertiesObject().getClusterNumber();
		double final_score = Double.MIN_VALUE;
        try
        {              
        	String filesDirectoryPath = ROOT_DIRECTORY + "src\\";
        	String positionFile = filesDirectoryPath + "chr1.pos"; //args[1];
        	String YRIFile = filesDirectoryPath  + "YRI_freqs_104.txt"; //args[2];
        	String CEUFile = filesDirectoryPath  + "CEU_freqs_106.txt"; //args[3];
        	String testFile = filesDirectoryPath  + "train_genos.txt"; //args[3];
        	String resultsFile = filesDirectoryPath + "train_pops.txt";
        	String realTestFile = filesDirectoryPath + "test_genos.txt";
        	String realResultsFile = filesDirectoryPath + "test_pops.txt";
        	String clusterFile = ROOT_DIRECTORY + "clusters\\clusters_" + CLUSTERS_SIZE + ".txt"; 
    
        	FileOutputStream outputFile = null;
        	PrintStream outputFilePrintStream = null;
        	if (OUT_FILE)
        	{
	        	// Stream to write file
	            outputFile = new FileOutputStream(filesDirectoryPath.substring(0,filesDirectoryPath.indexOf("src\\")) + "output.txt");
	            outputFilePrintStream = new PrintStream(outputFile);
        	}
        	
            //printSNPs(positionFile);
//            System.out.println("Start:");
            readSNPsData(positionFile,YRIFile,CEUFile);

            System.out.println("cluster is: " + clusterFile);
            if (new File(clusterFile).exists())
            {
            	readClustersFile(clusterFile);
            }
            else
            {
	            KMeans kmeans = new KMeans(CLUSTERS_SIZE, 1, positionFile, "\n");
	            List<Integer> clusters = kmeans.partition();
	            //System.out.println(kmeans.printResults());
	            int counter = 0;
	            int lastClusterIndex = 0;
	            int firstPosition = m_snps.get(0).getPosition();
	            for(int i = 0; i < clusters.size() ; i++)
	            {
	                if (lastClusterIndex != clusters.get(i))
	                {
//	                    System.out.println(lastClusterIndex + " " + counter);
//	                    System.out.println("Distance: " + (m_snps.get(i-1).getPosition()-firstPosition));
	                    lastClusterIndex = clusters.get(i);
	                    counter = 0;
	                    firstPosition = m_snps.get(i).getPosition();
	                }
	                else
	                {
	                	counter++;
	                }
	            }
//            	System.out.println(lastClusterIndex + " " + counter);
            
            	createOutputFile(clusters);
            	readClustersFile(clusterFile);
        	}
            
            if (!initialized)
            {
	            //calculateSignificats();
	            //TEST:
	            //readTestData(testFile, resultsFile);
	            //REAL:
	            readTestData(realTestFile, realResultsFile);
	            //initialized = true;
            }
            
          byte[][] haplotypes = new byte[2 * m_testCases.size()][m_snps.size()];
          
          for (int i  = 0 ; i < m_testCases.size() / 3 ; i++) {
          	PopulationCalculator.getHaplotypesForFamily(m_testCases.get(i*3 + 0).getSNPs(), m_testCases.get(i*3 + 1).getSNPs(), m_testCases.get(i*3 + 2).getSNPs(), m_snps, haplotypes, i);
          }
          
        List<Double> scores = new ArrayList<Double>();
  		for (int person = 0 ; person  < m_testCases.size() ; person++) 
  		{
  			System.out.println("Person number: " + person);
  	  		int errorCount = 0;
            boolean[] father = PopulationCalculator.calcCEUSource(m_snps, haplotypes[person * 2 ]);
            boolean[] mother = PopulationCalculator.calcCEUSource(m_snps, haplotypes[person * 2 + 1]);
            
            byte[] ret = new byte[m_snps.size()];
            for (int i = 0 ; i < ret.length ; i++) {
          	  if (father[i] == true) {
          		  ret[i]++;
          	  } 
          	  if (mother[i] == true) {
          		  ret[i]++;
          	  }
            }
            int first = -1;
            int counter = 0;
            int otherCounter = 0;
            if (person % 3 != 2) 
            {
	            for (int i = 0 ; i < m_snps.size() - 1; i++) 
	            {
	    			if (first == -1) 
	    			{
	    				first = i;
	    			}
	    			SNPData relevantSnp = m_snps.get(i), nextSnp = m_snps.get(i + 1);
	    			
	    			if (relevantSnp.getCluster() == nextSnp.getCluster()) 
	    			{
	    				continue;
	    			} 
	    			else 
	    			{
	    				Map<Byte,Double> result;
	
	                    List<Vector> patternList = new ArrayList<Vector>();
	                    for (int j = first ; j <= i ; j++) {
	                            double[] values = new double[1];
	                            values[0] = m_snps.get(j).getPosition();
	                            patternList.add(new Vector(values));
	                    }
	
	    	            KMeans kmeans = new KMeans(3, 1, patternList);
	    	            List<Integer> clusters = kmeans.partition();
	    	            
	    	            int middle1 = -1;
	    	            int middle2 = -1;
	    	            
	    	            for (int k = 0 ; k < clusters.size() - 1 ; k++) {
	    	            	if (clusters.get(k) != clusters.get(k+1)) {
	    	            		if (middle1 == -1) {
	    	            			middle1 = k;
	    	            		}
	    	            		else {
	    	            			middle2 = k;
	    	            			break;
	    	            		}
	    	            	}
	    	            }
	    	            
	    				result = PopulationCalculator.validateCluster(first, first + middle1, m_snps, m_testCases, person);
	    				if (!result.containsKey(ret[i])) {
	    					if (result.size() == 1) {
	    						setClusterToValue(first, first + middle1, ret, result.keySet().iterator().next());
	    						counter++;
	    					} else if (result.size() > 1) 
	    					{
	    						double highestScore = 0;
	    						byte highestIndex = 0;
	    						for (Entry<Byte,Double> e : result.entrySet()) 
	    						{
									if (e.getValue() > highestScore)
									{
										highestIndex = e.getKey();
									}
								}
	    						setClusterToValue(first, first + middle1, ret, highestIndex);
	    						otherCounter++;
	    					}
	    				}    				
	    				result = PopulationCalculator.validateCluster(first + middle1 + 1, first + middle2, m_snps, m_testCases, person);
	    				if (!result.containsKey(ret[i])) {
	    					if (result.size() == 1) {
	    						setClusterToValue(first+ middle1 + 1, first + middle2, ret, result.keySet().iterator().next());
	    						counter++;
	    					}
	    					else if (result.size() > 1) 
	    					{
	    						double highestScore = 0;
	    						byte highestIndex = 0;
	    						for (Entry<Byte,Double> e : result.entrySet()) 
	    						{
									if (e.getValue() > highestScore)
									{
										highestIndex = e.getKey();
									}
								}
	    						setClusterToValue(first+ middle1 + 1, first + middle2, ret, highestIndex);
	    						otherCounter++;
	    					}
	    				}   
	     				
	    				result = PopulationCalculator.validateCluster(first + middle2 + 1, i, m_snps, m_testCases, person);
	    				if (!result.containsKey(ret[i])) {
	    					if (result.size() == 1) {
	    						setClusterToValue(first + middle2 + 1, i, ret, result.keySet().iterator().next());
	    						counter++;
	    					} else if (result.size() > 1)
	       					{
	    						double highestScore = 0;
	    						byte highestIndex = 0;
	    						for (Entry<Byte,Double> e : result.entrySet()) 
	    						{
									if (e.getValue() > highestScore)
									{
										highestIndex = e.getKey();
									}
								}
	    						setClusterToValue(first + middle2 + 1, i, ret, highestIndex);
	    						otherCounter++;
	    					}
	    				}    				
	
	    				//get haplotype source estimation for cluster (first, i)
	    				first = -1;
	    			}
	    		}
				Map<Byte,Double> result;
				
	            List<Vector> patternList = new ArrayList<Vector>();
	            for (int j = first ; j <=  m_snps.size() - 1 ; j++) {
	                    double[] values = new double[1];
	                    values[0] = m_snps.get(j).getPosition();
	                    patternList.add(new Vector(values));
	            }

	            KMeans kmeans = new KMeans(3, 1, patternList);
	            List<Integer> clusters = kmeans.partition();
	            
	            int middle1 = -1;
	            int middle2 = -1;
	            
	            for (int k = 0 ; k < clusters.size() - 1 ; k++) {
	            	if (clusters.get(k) != clusters.get(k+1)) {
	            		if (middle1 == -1) {
	            			middle1 = k;
	            		}
	            		else {
	            			middle2 = k;
	            			break;
	            		}
	            	}
	            }
	            
				result = PopulationCalculator.validateCluster(first, first + middle1, m_snps, m_testCases, person);
				if (!result.containsKey(ret[m_snps.size() - 1])) {
					if (result.size() == 1) {
						setClusterToValue(first, first + middle1, ret, result.keySet().iterator().next());
						counter++;
					} else if (result.size() > 1) 
					{
						double highestScore = 0;
						byte highestIndex = 0;
						for (Entry<Byte,Double> e : result.entrySet()) 
						{
							if (e.getValue() > highestScore)
							{
								highestIndex = e.getKey();
							}
						}
						setClusterToValue(first, first + middle1, ret, highestIndex);
						otherCounter++;
					}
				}    				
				result = PopulationCalculator.validateCluster(first + middle1 + 1, first + middle2, m_snps, m_testCases, person);
				if (!result.containsKey(ret[m_snps.size() - 1])) {
					if (result.size() == 1) {
						setClusterToValue(first+ middle1 + 1, first + middle2, ret, result.keySet().iterator().next());
						counter++;
					}
					else if (result.size() > 1) 
					{
						double highestScore = 0;
						byte highestIndex = 0;
						for (Entry<Byte,Double> e : result.entrySet()) 
						{
							if (e.getValue() > highestScore)
							{
								highestIndex = e.getKey();
							}
						}
						setClusterToValue(first+ middle1 + 1, first + middle2, ret, highestIndex);
						otherCounter++;
					}
				}   
					
				result = PopulationCalculator.validateCluster(first + middle2 + 1, m_snps.size() - 1, m_snps, m_testCases, person);
				if (!result.containsKey(ret[m_snps.size() - 1])) {
					if (result.size() == 1) {
						setClusterToValue(first + middle2 + 1, m_snps.size() - 1, ret, result.keySet().iterator().next());
						counter++;
					} else if (result.size() > 1)
						{
						double highestScore = 0;
						byte highestIndex = 0;
						for (Entry<Byte,Double> e : result.entrySet()) 
						{
							if (e.getValue() > highestScore)
							{
								highestIndex = e.getKey();
							}
						}
						setClusterToValue(first + middle2 + 1, m_snps.size() - 1, ret, highestIndex);
						otherCounter++;
					}
				}    
				//end of last iteration

            }
    		
    		PopulationCalculator.chancesMatrix = null;
    		
  			byte[] resSnps = m_testCases.get(person).getResultSNPs();
  			for (int i = 0; i < m_snps.size(); i++)
  			{
  				if (ret[i] != resSnps[i])
  				{
  					errorCount++;
  				}
  			}
  			
  			int correctGuesses = m_snps.size() - errorCount;
  //			System.out.println("Correct guesses are " + (double)correctGuesses / m_snps.size());
  			scores.add((double)correctGuesses / m_snps.size());
  			
  			if (person == 0)
  			{
  				printFile("personOneGuesses", ret);
  			}
  			
  			if (OUT_FILE)
        	{
	  			//add person to out stream
	  			StringBuffer outPerson = new StringBuffer();
	  			for (byte b : ret) 
	  			{
	  				outPerson.append("  " + b);
				}
	  			outputFilePrintStream.println(outPerson);
        	}
  		}
  			
  		double totalScores = 0;
		  for (Double d : scores) 
		  {
			  totalScores += d;
		  }
		  final_score = totalScores / scores.size();
		  System.out.println("Average is " +  totalScores / scores.size());
//          System.out.println("End");
                       
          if (OUT_FILE)
      	  {
	          // Close our output stream
	          outputFile.close();
      	  }
        }
        catch (IOException e)
        {
            System.err.println ("Unable to write to file");
            System.exit(-1);

        }    
        catch (Exception e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return final_score;

	}
    private static void setClusterToValue(int first, int last, byte[] ret, byte result) {
		for (int i = first ; i <= last ; i++) {
			ret[i] = result;
		}
	}

	private static void createOutputFile(List<Integer> clusters)
    {
        // Stream to write file
        FileOutputStream fout;       

        try
        {
            // Open an output stream
            fout = new FileOutputStream (ROOT_DIRECTORY + "clusters\\clusters_" + CLUSTERS_SIZE + ".txt");
            PrintStream p = new PrintStream(fout);

            for (int i = 0; i < clusters.size() ; i++)
            {
                // Print a line of text
                p.println(i + "\t" + clusters.get(i));

            }
            // Close our output stream
            fout.close();       
        }
        // Catches any error conditions
        catch (IOException e)
        {
            System.err.println ("Unable to write to file");
            System.exit(-1);

        }       
    }

    private static void readClustersFile(String clusterFile) {

		FileReader fr;
		
		try {
		
			//	get positions
			fr = new FileReader(clusterFile);
			BufferedReader br = new BufferedReader(fr);
			String s;
			while((s = br.readLine()) != null)
			{
				String[] parts = s.split("\t");
				m_snps.get(Integer.parseInt(parts[0])).setCluster(Integer.parseInt(parts[1]));
			}
			fr.close();
		} 
		catch (Exception e ) 
		{
			e.printStackTrace();
		}
    }
    
	private static void printSNPs(String positionFile) throws NumberFormatException, IOException 
	{
		FileReader fr;
		
		//get positions
		fr = new FileReader(positionFile);

		BufferedReader br = new BufferedReader(fr);
		String s;
		long[] location = new long[48827];
		int i = 0;
		while((s = br.readLine()) != null)
		{
			location[i] = Long.parseLong(s);
			i++;
		}
		fr.close();
		
		MotifGraph m = new MotifGraph(247165315);
		m.showFrame(location);
		
		try
		{
			Thread.sleep(100);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		
	}

    private static void readTestData(String testFile, String resultsFile) throws FileNotFoundException, IOException 
    {
            int counter = 0;

             FileReader fr = new FileReader(testFile);

            BufferedReader br = new BufferedReader(fr);
            String s;
            while((s = br.readLine()) != null)
            {
                    s = s.trim();
                    String[] snps = s.split("  ");
                    byte[] data = new byte[snps.length];
                    
                    int i=0;
                    for (String snp : snps) 
                    {
                            data[i] = Byte.parseByte(snp);
                            i++;
                    }
                    
                    PersonData person = new PersonData(counter);
                    person.setSNPs(data);
                    m_testCases.add(person);
                    counter++;
            }
            fr.close();
            
    		fr = new FileReader(resultsFile);
    		counter = 0;
    		br = new BufferedReader(fr);
    		while ((s = br.readLine()) != null)
    		{
    			s = s.trim();
    			String[] snps = s.split("  ");
    			byte[] data = new byte[snps.length];

    			int i = 0;
    			for (String snp : snps)
    			{
    				data[i] = Byte.parseByte(snp);
    				i++;
    			}

    			PersonData person = m_testCases.get(counter);
    			person.setResultSNPs(data);
    			counter++;
    		}
    		fr.close();            
    }

	private static void readSNPsData(String positionFile, String YRIFile, String CEUFile) throws FileNotFoundException, IOException 
	{
		
		FileReader fr;
		int counter = 0;
		
		//get positions
		fr = new FileReader(positionFile);

		BufferedReader br = new BufferedReader(fr);
		String s;
		while((s = br.readLine()) != null)
		{
			SNPData data = new SNPData(counter);
			data.setPosition(Integer.parseInt(s.trim()));
			m_snps.put(counter, data);
			counter++;
		}
		fr.close();
		
		//get CEU data
		counter = 0;
		fr = new FileReader(CEUFile);
		br = new BufferedReader(fr);
		while((s = br.readLine()) != null)
		{
			m_snps.get(counter).setCEU(Float.parseFloat(s.trim()));
			counter++;
		}
		fr.close();
		
		//get YRI data
		counter = 0;
		fr = new FileReader(YRIFile);
		br = new BufferedReader(fr);
		while((s = br.readLine()) != null)
		{
			m_snps.get(counter).setYRI(Float.parseFloat(s.trim()));
			counter++;
		}
		fr.close();
	}

	private static void calculateSignificats() 
	{
		for (int i = 0; i < m_snps.size(); i++) 
		{
			/*
			 if (((Math.abs(m_snps.get(i).getCEU() - m_snps.get(i).getYRI())> DIFF_THRESHOLD) 
					&& ((m_snps.get(i).getYRI() > HIGH_VALUE_THRESHOLD) || (m_snps.get(i).getCEU() > HIGH_VALUE_THRESHOLD) ||
							(m_snps.get(i).getYRI() < LOW_VALUE_THRESHOLD) || (m_snps.get(i).getCEU() < LOW_VALUE_THRESHOLD))) 
							|| (m_snps.get(i).getCEU() == 0.0) || (m_snps.get(i).getCEU() == 1.0) || (m_snps.get(i).getYRI() == 0.0) || (m_snps.get(i).getYRI() == 1.0))
			 */
			if ((Math.abs(m_snps.get(i).getCEU() - m_snps.get(i).getYRI())> DIFF_THRESHOLD) 
					&& ((m_snps.get(i).getYRI() > HIGH_VALUE_THRESHOLD) || (m_snps.get(i).getCEU() > HIGH_VALUE_THRESHOLD) ||
							(m_snps.get(i).getYRI() < LOW_VALUE_THRESHOLD) || (m_snps.get(i).getCEU() < LOW_VALUE_THRESHOLD)))
			{
				m_significant.add(i);
				System.out.println(String.format("SNP is %d \t Position:%d \t CEU: %f \t YRI: %f", i + 1, m_snps.get(i).getPosition(), m_snps.get(i).getCEU(), m_snps.get(i).getYRI()));
			}
		}
	}

	private static void printFile(String filename, byte[] arrayToPrint) throws IOException
	{
		int counter = 0;

		FileWriter fw = new FileWriter(filename);

		BufferedWriter bw = new BufferedWriter(fw);
		String s;
		
		int x = arrayToPrint.length;
		
		for (int i = 0; i < arrayToPrint.length ; i++)
		{
			int toWrite = arrayToPrint[i];
			bw.write(toWrite + "");
			bw.newLine();
		}
		
		fw.close();
	}
	
}
